package talent.util;


//import cn.iocoder.boot.kmeans.Talent;
import org.springframework.stereotype.Component;
import talent.entity.Talent;
import talent.vo.TalentVO;

import java.util.*;
import java.util.stream.Collectors;

@Component
public class KMeansPlusPlusUtil {

    public List<List<Talent>> clusterTalents(List<Talent> talents, int k) {
        if (talents == null || talents.isEmpty() || k <= 0) {
            return Collections.emptyList();
        }

        // 1. 初始化聚类中心
        List<Talent> centers = initializeCenters(talents, k);

        // 2. 迭代聚类
        List<List<Talent>> clusters = new ArrayList<>();
        boolean changed;
        do {
            // 分配点到最近的聚类中心
            clusters = assignPointsToClusters(talents, centers);

            // 重新计算聚类中心
            List<Talent> newCenters = calculateNewCenters(clusters);

            // 检查是否收敛
            changed = !centers.equals(newCenters);
            centers = newCenters;
        } while (changed);

        return clusters;
    }

    public List<TalentVO> findOptimalTalents(List<Talent> talents, int maxSalary, int expectedCount) {
        // 使用空间优化的动态规划算法
        double[] dp = new double[maxSalary + 1];
        int n = talents.size();
        boolean[][] keep = new boolean[n][maxSalary + 1];

        // 按价值评分排序
        List<Talent> sortedTalents = talents.stream()
                .sorted(Comparator.comparing(this::calculateValueScore).reversed())
                .collect(Collectors.toList());

        for (int i = 0; i < n; i++) {
            Talent talent = sortedTalents.get(i);
            int weight = talent.getExpectedSalary();
            double value = calculateValueScore(talent);

            for (int w = maxSalary; w >= weight; w--) {
                if (dp[w - weight] + value > dp[w]) {
                    dp[w] = dp[w - weight] + value;
                    keep[i][w] = true;
                }
            }
        }

        // 回溯找出选中的物品
        List<Talent> result = new ArrayList<>();
        int w = maxSalary;
        for (int i = n - 1; i >= 0; i--) {
            if (keep[i][w]) {
                result.add(sortedTalents.get(i));
                w -= sortedTalents.get(i).getExpectedSalary();
            }
        }

        // 转换为VO对象
        return result.stream()
                .map(this::convertToVO)
                .limit(expectedCount)
                .collect(Collectors.toList());
    }

    private List<Talent> initializeCenters(List<Talent> talents, int k) {
        Random random = new Random();
        List<Talent> centers = new ArrayList<>();

        // 第一个中心随机选择
        centers.add(talents.get(random.nextInt(talents.size())));

        // 使用k-means++算法选择剩余中心
        for (int i = 1; i < k; i++) {
            double[] distances = new double[talents.size()];
            double sum = 0;

            for (int j = 0; j < talents.size(); j++) {
                double minDist = Double.MAX_VALUE;
                for (Talent center : centers) {
                    double dist = calculateDistance(talents.get(j), center);
                    if (dist < minDist) {
                        minDist = dist;
                    }
                }
                distances[j] = minDist * minDist;
                sum += distances[j];
            }

            // 轮盘赌选择下一个中心
            double r = random.nextDouble() * sum;
            double cumulativeSum = 0;
            for (int j = 0; j < distances.length; j++) {
                cumulativeSum += distances[j];
                if (cumulativeSum >= r) {
                    centers.add(talents.get(j));
                    break;
                }
            }
        }

        return centers;
    }

    private List<List<Talent>> assignPointsToClusters(List<Talent> talents, List<Talent> centers) {
        List<List<Talent>> clusters = new ArrayList<>();
        for (int i = 0; i < centers.size(); i++) {
            clusters.add(new ArrayList<>());
        }

        for (Talent talent : talents) {
            int nearestCenterIndex = 0;
            double minDistance = Double.MAX_VALUE;

            for (int i = 0; i < centers.size(); i++) {
                double distance = calculateDistance(talent, centers.get(i));
                if (distance < minDistance) {
                    minDistance = distance;
                    nearestCenterIndex = i;
                }
            }

            clusters.get(nearestCenterIndex).add(talent);
        }

        return clusters;
    }

    private List<Talent> calculateNewCenters(List<List<Talent>> clusters) {
        List<Talent> newCenters = new ArrayList<>();

        for (List<Talent> cluster : clusters) {
            if (cluster.isEmpty()) {
                continue;
            }

            // 计算聚类中所有点的平均值作为新中心
            Talent newCenter = new Talent();
            int count = cluster.size();

            // 计算各项属性的平均值
            int totalProjects = cluster.stream().mapToInt(Talent::getProjectCount).sum();
            int totalExp = cluster.stream().mapToInt(Talent::getWorkExperience).sum();
            int totalEdu = cluster.stream().mapToInt(Talent::getEducationLevel).sum();
            int totalSalary = cluster.stream().mapToInt(Talent::getExpectedSalary).sum();

            newCenter.setProjectCount(totalProjects / count);
            newCenter.setWorkExperience(totalExp / count);
            newCenter.setEducationLevel(totalEdu / count);
            newCenter.setExpectedSalary(totalSalary / count);

            newCenters.add(newCenter);
        }

        return newCenters;
    }

    private double calculateDistance(Talent a, Talent b) {
        // 欧式距离计算
        double skillDiff = a.getProjectCount() - b.getProjectCount();
        double expDiff = a.getWorkExperience() - b.getWorkExperience();
        double eduDiff = a.getEducationLevel() - b.getEducationLevel();
        double salaryDiff = (a.getExpectedSalary() - b.getExpectedSalary()) / 10000.0;

        return Math.sqrt(
                skillDiff * skillDiff +
                        expDiff * expDiff +
                        eduDiff * eduDiff +
                        salaryDiff * salaryDiff
        );
    }

    private double calculateValueScore(Talent talent) {
        return talent.getProjectCount() * 0.3 +
                talent.getWorkExperience() * 0.2 +
                talent.getEducationLevel() * 0.1 +
                (1 - talent.getExpectedSalary() / 30000.0) * 0.4;
    }

    private TalentVO convertToVO(Talent talent) {
        TalentVO vo = new TalentVO();
        vo.setId(talent.getId());
        vo.setName(talent.getName());
        vo.setProjectCount(talent.getProjectCount());
        vo.setWorkExperience(talent.getWorkExperience());
        vo.setEducationLevel(talent.getEducationLevel());
        vo.setExpectedSalary(talent.getExpectedSalary());
        vo.setRankingScore(calculateValueScore(talent));
        return vo;
    }
}
